using System;
using System.Collections.Generic;
using System.IO;

namespace T7G_4_Win
{
    class Groovie
    {
        /// <summary>
        /// File handle to the current Groovie script
        /// </summary>
        BinaryReader stream;

        /// <summary>
        /// Path to T7G file directory containing files
        /// </summary>
        private string path = Properties.Settings.Default.T7GPath;

        /// <summary>
        /// Script variables containing environment data
        /// </summary>
        private byte[] vars = new byte[0x400];

        private Stack<long> callStack;

        #region mouse related
        
        public struct mouseStatus
        {
            public short x, y;
            public System.Drawing.Point position;
            public SdlDotNet.Input.MouseButton button;
        }

        private mouseStatus mouseEvent;

        private struct stdRegion
        {
            public ushort jumpAddress;
            public byte cursor;
        }

        private stdRegion[] standardHotspot = new stdRegion[3];

        #endregion
        /// <summary>
        /// GRV bitflags
        /// </summary>
        [Flags]
        public enum bitFlags
        {
            Bit0 = (1 << 0),
            Bit1 = (1 << 1),
            Bit2 = (1 << 2),
            Bit3 = (1 << 3),
            Bit4 = (1 << 4),
            Bit5NoStill = (1 << 5),
            Bit6 = (1 << 6),
            Bit7 = (1 << 7),
            Bit8JustStill = (1 << 8),
            Bit9 = (1 << 9)
        }

        private bitFlags flags = 0;

        public struct specialVDX
        {
            public byte op1C04;
            public byte op400D;
            public byte op5060;
            public byte op5098;
            public byte op206D;
            public byte op1C05;
            public byte op2402;
            public byte op260D;
        }

        private specialVDX VDXFlags = new specialVDX();

        private Groovie sub;

        //private GJD gjd = null;
        private VDX vdx = null;
        public VDX getVDX()
        {
            
            return (sub == null ? vdx : sub.getVDX());
        }

        /// <summary>
        /// Continue running thead?
        /// </summary>
        private bool doThread = true;

        /// <summary>
        /// (X)MIDI/CDA playback object
        /// </summary>
        private GrvMusic music;

        /// <summary>
        /// Back-reference
        /// </summary>
        T7G game;

        /// <summary>
        /// Jump-to location upon mouseclick
        /// </summary>
        long newLoc = -1;

        /// <summary>
        /// Parse a Groovie script file
        /// </summary>
        /// <param name="filename">Filename to parse (inc extension)</param>
        public Groovie(string filename, T7G window)
        {
            game = window;

            // File handle
            stream = new BinaryReader(new BufferedStream(new FileStream(path + filename, FileMode.Open, FileAccess.Read, FileShare.Read)));

            // Clear variables
            for (int i = 0; i < 0x400; i++)
            {
                vars[i] = 0;
            }

            callStack = new Stack<long>();
        }

        ~Groovie()
        {
            stream.Close();
            music = null;
            vdx = null;
        }


        public void execute()
        {
            bool firstBit;
            byte op;
            GJD gjd = null;
            long inputLoopPos = 0;
            while (doThread)
            {
                
                if (newLoc >= 0)
                {
                    stream.BaseStream.Seek(newLoc, SeekOrigin.Begin);
                    game.removeHotspot();
                    newLoc = -1;
                }
                
                
                // Get opcode
                op = stream.ReadByte();
                // Check if first bit set
                if ((op & 128) == 128)
                {
                    op &= 127;
                    firstBit = true;
                }
                else
                {
                    firstBit = false;
                }
                
                /*
                 * if (something)
                 *   vars[0x103]++;
                 * 
                 */

                if (VDXFlags.op400D == 2)
                {
                    // Get a GJD?
                    VDXFlags.op400D = 0;
                }

                /* if (.op1c05 > 0) ?
                 *   do things
                 */

                /* if (.op2042 & 0x80)
                 *   do stuff/jump
                 *   .op2402 = 0
                 */


                //Console.WriteLine("Opcode 0x{0:X}", op);

                switch (op)
                {
                    #region NOP's
                    case 0x28:
                    case 0x4A:
                    case 0x4E:
                    case 0x4F:
                    case 0x50:
                    case 0x51:
                    case 0x55:
                        // 2 byte NOP
                        {
                            ushort arg = stream.ReadUInt16();
                        }
                        break;

                    case 0x1E:
                    case 0x48:
                    case 0x4B:
                        // 1 Byte NOP
                        {
                            byte arg = stream.ReadByte();
                        }
                        break;

                    case 0x01:
                    case 0x46:
                    case 0x49:
                    case 0x47:
                        // NOP
                        break;

                    case 0x59:
                        // Variable size NOP
                        {
                            ushort arg1, arg2;
                            if (firstBit)
                                arg1 = stream.ReadByte();
                            else
                                arg1 = stream.ReadUInt16();
                            arg2 = stream.ReadByte();
                            Console.WriteLine("NOP variable: 0x{0:X} with args 0x{1:X}, 0x{2:X}", op, arg1, arg2);
                        }
                        break;

                    #endregion

                    #region Bitflags (3, 5, 6, 7, A, 35, 57)

                    case 0x03:
                        // Enable bit 9
                        flags |= bitFlags.Bit9;
                        break;

                    case 0x05:
                        // Bit 8 (just still ?)
                        flags |= bitFlags.Bit8JustStill;
                        break;

                    case 0x06:
                        // Bit 6
                        flags |= bitFlags.Bit6;
                        break;

                    case 0x07:
                        // Bit 7 - Just audio?
                        flags |= bitFlags.Bit7;
                        break;

                    case 0x0A:
                        // Bit 5 - skip stills?
                        flags |= bitFlags.Bit5NoStill;
                        break;

                    case 0x35: // just audio
                        flags &= ~(bitFlags.Bit7);
                        break;

                    case 0x57:
                        // Bit 4
                        flags |= bitFlags.Bit4;
                        // Do other stuff
                        // Load GJD?
                        throw new Exception("Do other things");
                        break;

                    case 0x58:
                        flags |= bitFlags.Bit4;
                        // other things
                        // GJD if something not set
                        throw new Exception("Do other things too");
                        break;


                    #endregion

                    #region Mouse clicks - D, E, F, 10, 11, 12, 2C, 2D, 30, 44, 45, 53)

                    case 0x0D:
                        // Mark a rectangle clickable.  On hover, show cursor arg6.  Jump to arg5 on click
                        {
                            ushort left = stream.ReadUInt16();
                            ushort top = stream.ReadUInt16();
                            ushort right = stream.ReadUInt16();
                            ushort bottom = stream.ReadUInt16();
                            ushort jump = stream.ReadUInt16();
                            byte cursor = stream.ReadByte();
                            game.addHotspot(new System.Drawing.Rectangle(left, top, right - left, bottom - top), cursor, jump);
                        }
                        break;
                    case 0x0E:
                        // Left 100px clickable 
                        game.standardHotspot(T7G.mouseSpots.left, 1, stream.ReadUInt16());
                        break;

                    case 0x0F:
                        // Right 100px clickable 
                        game.standardHotspot(T7G.mouseSpots.right, 2, stream.ReadUInt16());
                        break;

                    case 0x10:
                    case 0x11:
                        // Middle 240px clickable
                        game.addHotspot(new System.Drawing.Rectangle(200, 80, 240, 320), 0, stream.ReadUInt16());
                        break;

                    case 0x12:
                        // Current pos clickable
                        game.addHotspot(new System.Drawing.Rectangle(0, 80, 640, 320), 0, stream.ReadUInt16());
                        break;

                    case 0x30:
                        // Bottom 80px clickable
                        game.standardHotspot(T7G.mouseSpots.bottom, 0, stream.ReadUInt16());
                        break;


                    case 0x2C:
                        {
                            // Destination and cursor for top hotspot
                            ushort jump = stream.ReadUInt16();
                            byte cursor = stream.ReadByte();
                            game.standardHotspot(T7G.mouseSpots.top, cursor, jump);
                        }
                        break;

                    case 0x2D:
                        {
                            // Destination and cursor for bottom hotspot
                            //standardHotspot[2].jumpAddress = stream.ReadUInt16();
                            //standardHotspot[2].cursor = stream.ReadByte();
                            ushort jump = stream.ReadUInt16();
                            byte cursor = stream.ReadByte();
                            game.standardHotspot(T7G.mouseSpots.bottom, cursor, jump);
                        }
                        break;

                    case 0x44:
                        {
                            // Destination for right hotspot

                            //standardHotspot[1].jumpAddress = stream.ReadUInt16();
                            game.standardHotspot(T7G.mouseSpots.right, null, stream.ReadUInt16());

                            //throw new Exception("Action for right hotspot");
                        }
                        break;

                    case 0x45:
                        {
                            // Destination for left hotspot

                            //standardHotspot[3].jumpAddress = stream.ReadUInt16();
                            game.standardHotspot(T7G.mouseSpots.left, null, stream.ReadUInt16());
                            //throw new Exception("Action for left hotspot");
                        }
                        break;

                    case 0x53:
                        // Load cursor stuff
                        stream.ReadBytes(10);
                        throw new Exception("Cursor manipulations");
                        break;

                    case 0x0B:
                        // Start input loop
                        if (inputLoopPos == 0)
                            inputLoopPos = stream.BaseStream.Position - 1;

                        game.removeHotspot();

                        // Something midi related here

                        // Play loop midi perhaps - if not midi playing already?



                        Console.WriteLine("Start input loop");
                        break;

                    case 0x0C:
                        // Keyboard related
                        Console.WriteLine("0x0C: Keyboard input 0x{0:X}, jump to 0x{1:X}???", stream.ReadByte(), stream.ReadUInt16());
                        //game.addHotspot(stream.ReadByte(), stream.ReadUInt16());
                        break;

                    case 0x13:
                        // End input loop
                        // Checks if mouse inputs have passed
                        // Jump to input loop start

                        //stream.BaseStream.Seek(inputLoopPos, SeekOrigin.Begin);
                        newLoc = -1;
                        while (newLoc < 0)
                            System.Threading.Thread.Sleep(100);

                        break;

                    #endregion

                    case 0x02:
                        // Play "XMI".  Unfortunately that's a little hard at the moment so play MIDI conversion
                        {
                            ushort offset = stream.ReadUInt16();
                            ushort gjdIndex = (ushort)(offset >> 10);
                            ushort file = (ushort)(offset & 0x3FF);
                            if (music == null)
                                music = new GrvMusic(game);

                            GJD midi = new GJD(gjdIndex);
                            GJD.RLData rl;
                            midi.getVDX(file, out rl);
                            music.MIDI(rl.filename);
                        }
                        break;

                    case 0x04:
                        // Fade out                        
                        //game.removeVDX();
                        System.Threading.Thread.Sleep(20);
                        game.screen.Alpha = (byte)255;
                        
                        Console.WriteLine("Palette fade-out.  TODO");
                        break;

                    case 0x08:
                        // Loop XMI file?
                        {
                            if (music == null)
                                music = new GrvMusic(game);

                            ushort offset = stream.ReadUInt16();
                            ushort gjdIndex = (ushort)(offset >> 10);
                            ushort file = (ushort)(offset & 0x3FF);
                            GJD midi = new GJD(gjdIndex);
                            GJD.RLData rl;
                            midi.getVDX(file, out rl);
                            music.MIDILoop(rl.filename);
                            Console.WriteLine("Set the loop XMI");
                        }
                        break;

                    #region Display related

                    case 0x22:
                        // Copy screen to back buffer
                        Console.WriteLine("copy buffer?");
                        game.gameBuffer.Blit(game.gameArea);
                        break;

                    case 0x37:
                        // Copy screen to game buffer
                        {
                            ushort left = stream.ReadUInt16(), top = stream.ReadUInt16(), right = stream.ReadUInt16(), bottom = stream.ReadUInt16();
                            Console.WriteLine("Copy screen to game buffer");
                            //game.gameBuffer.Blit(game.gameArea);
                        }
                        break;

                    case 0x09:
                        // Play VDX
                        {
                            ushort offset = stream.ReadUInt16();
                            ushort gjdIndex = (ushort)(offset >> 10);
                            ushort file = (ushort)(offset & 0x3FF);
                            //if (gjd == 9 || gjd == 7)
                            //    flags |= bitFlags.JustStill;

                            if ((gjd == null) || (gjd.Index != gjdIndex))
                            {
                                gjd = new GJD(gjdIndex);
                            }

                            {
                                GJD.RLData r;
                                gjd.getVDX(file, out r);
                                Console.WriteLine("Play VDX {0} (0x{1:X}) with flags {2} ", r.filename, offset, flags.ToString());
                            }

                            if (offset == 0x400D)
                            {
                                // Set 400D flag
                                VDXFlags.op400D = 1;
                                Console.WriteLine("Special VDX");
                            }
                            else if (offset == 0x5060)
                            {
                                Console.WriteLine("Special VDX");
                                if (VDXFlags.op5060 != 0)
                                    continue;
                                else
                                    VDXFlags.op5060 = 1;
                            }
                            else if (offset == 0x5098)
                            {
                                Console.WriteLine("Special VDX");
                                if ((VDXFlags.op5098) != 0)
                                    continue;
                                else
                                    VDXFlags.op5098 = 1;
                            }
                            else if (offset == 0x1C04 || offset == 0x1C03)
                            {
                                Console.WriteLine("Special VDX");
                                if ((VDXFlags.op1C04) != 0)
                                {
                                    //sub_401c99
                                    continue;
                                }
                            }
                            else if (offset == 0x1C05)
                            {
                                Console.WriteLine("Special VDX");
                                VDXFlags.op1C05 = 1;
                            }
                            else if (offset == 0x2402)
                            {
                                Console.WriteLine("Special VDX");
                                VDXFlags.op2402 = 1;
                            }
                            else if (offset == 0x1426)
                            {
                                Console.WriteLine("Special VDX");
                                VDXFlags.op2402 = 0;
                            }
                            else if (offset == 0x206D || offset == 0x2001)
                            {
                                Console.WriteLine("Special VDX");
                                VDXFlags.op206D = 0;
                                // Clear a flag
                            }

                            GJD.RLData rl;

                            vdx = new VDX(gjd.getVDX(file, out rl), rl, flags, (vdx == null ? null : vdx.surface));
                            vdx.play();
                            while (vdx.playing)
                                System.Threading.Thread.Sleep(50);

                            flags = 0;
                        }
                        break;

                    case 0x1C:
                        {
                            // Play VDX - 2nd type (simple)
                            ushort offset = stream.ReadUInt16();
                            ushort gjdIndex = (ushort)(offset >> 10);
                            ushort file = (ushort)(offset & 0x3FF);

                            flags |= bitFlags.Bit1;
                            if (firstBit)
                                flags |= bitFlags.Bit2;
                            flags &= ~(flags & bitFlags.Bit7);

                            if ((gjd == null) || (gjd.Index != gjdIndex))
                            {
                                gjd = new GJD(gjdIndex);
                            }

                            {
                                GJD.RLData r;
                                gjd.getVDX(file, out r);
                                Console.WriteLine("Play VDX {0} (0x{1:X}) with flags {2}", r.filename, offset, flags.ToString());
                            }

                        }
                        break;


                    case 0x26:
                        throw new Exception("Other awkward");

                        // Load a GJD here somewhere as well
                        break;


                    case 0x27:
                        // Turns on bit 2 of bitflags (and bit 3 if firstbit is set). VDX file name (in a .gjd) follows: find it, then play it
                        {
                            flags |= bitFlags.Bit2;
                            if (firstBit)
                                flags |= bitFlags.Bit3;
                            byte t;
                            string f = "";
                            do
                            {
                                t = stream.ReadByte();
                                f += t.ToString();
                            } while (t != 0);
                            throw new Exception("Needs more work here - needs pre-opened GJD?");

                        }
                        break;

                    #endregion

                    case 0x14:
                        // Random number generation
                        {
                            ushort offset;
                            if (firstBit)
                                offset = stream.ReadByte();
                            else
                                offset = stream.ReadUInt16();
                            vars[offset] = (byte)(new System.Random().Next(stream.ReadByte()));
                        }
                        break;

                    case 0x15:
                        // Jump
                        {
                            ushort jump = stream.ReadUInt16();
                            stream.BaseStream.Seek(jump, SeekOrigin.Begin);
                        }
                        break;

                    case 0x16:
                        // Reads a string into the script variable specified in the first 2 (or 1) bytes
                        {
                            ushort offset;
                            if (firstBit)
                                offset = stream.ReadByte();
                            else
                                offset = stream.ReadUInt16();

                            byte b;
                            do
                            {
                                b = awkward1();
                                vars[offset] = b;
                                offset++;
                                stream.BaseStream.Seek(-1, SeekOrigin.Current);
                                b = stream.ReadByte();
                            } while ((b & 0x80) == 0);
                                
                        }
                        break;

                    case 0x17:
                        // Return from CALL
                        vars[0x102] = stream.ReadByte();
                        stream.BaseStream.Seek(callStack.Pop(), SeekOrigin.Begin);
                        break;

                    case 0x18:
                        // CALL
                        {
                            callStack.Push(stream.BaseStream.Position + 2);
                            ushort offset = stream.ReadUInt16();
                            stream.BaseStream.Seek(offset, SeekOrigin.Begin);
                        }
                        break;

                    case 0x19:
                        // Sleep
                        System.Threading.Thread.Sleep(stream.ReadUInt16() * 3);
                        break;

                    #region String compare + jump (op 1A and 23)
                    case 0x1A:
                    case 0x23:
                        {
                            // Get var location
                            ushort offset;
                            if (firstBit)
                                offset = stream.ReadByte();
                            else
                                offset = stream.ReadUInt16();

                            byte match = 1, t;

                            do
                            {
                                // Read byte
                                t = awkward1();
                                if (t != vars[offset])
                                    match = 0; // Store match state

                                // Break when needed
                                offset++;

                                stream.BaseStream.Seek(-1, SeekOrigin.Current);
                                t = stream.ReadByte();
                            } while ((t & 0x80) != 0x80);

                            offset = stream.ReadUInt16();
                            // JE or JNE?
                            if (op == 0x1A)
                            {
                                if (match == 0)
                                {
                                    // strcmp-JNE
                                    stream.BaseStream.Seek(offset, SeekOrigin.Begin);
                                }
                            }
                            else
                            {
                                if (match == 1)
                                {
                                    // strcmp-JE
                                    stream.BaseStream.Seek(offset, SeekOrigin.Begin);
                                }
                            }

                        }
                        break;
                    #endregion

                    case 0x1B:
                        // XOR
                        {
                            ushort offset;
                            if (firstBit)
                                offset = stream.ReadByte();
                            else
                                offset = stream.ReadUInt16();

                            byte t;
                            do
                            {
                                t = stream.ReadByte();
                                t &= 0x4F;
                                Console.WriteLine("Changed behavior 1B?");
                                vars[offset] ^= t;
                                offset++;
                                stream.BaseStream.Seek(-1, SeekOrigin.Current);
                                t = stream.ReadByte();
                            } while ((t & 0x80) == 0);
                        }
                        break;



                    case 0x1D:
                        // Swap to variables
                        {
                            ushort offa, offb;
                            byte t;
                            if (firstBit)
                                offa = stream.ReadByte();
                            else
                                offa = stream.ReadUInt16();

                            offb = stream.ReadUInt16();
                            t = vars[offa];
                            vars[offa] = vars[offb];
                            vars[offb] = t;
                        }
                        break;

                    case 0x1F:
                        // INC
                        if (firstBit)
                            vars[stream.ReadByte()]++;
                        else
                            vars[stream.ReadUInt16()]++;

                        break;

                    case 0x20:
                        // DEC
                        if (firstBit)
                            vars[stream.ReadByte()]--;
                        else
                            vars[stream.ReadUInt16()]--;

                        break;

                    case 0x21:
                        {
                            // call something
                            ushort offset=0; /* = (ushort)functionCall();*/
                            if (offset > 0x09)
                            {
                                offset -= 7;
                            }
                            // loc_4031CF
                            offset = vars[offset + 0x19];
                            byte match = 1, x, y, z;

                            // loc_4031F6
                            do
                            {
                                x = vars[offset];
                                y = awkward1();
                                offset++;
                                if (x != y)
                                    match = 0;

                                // loc_403237
                                stream.BaseStream.Seek(-1, SeekOrigin.Current);
                                z = stream.ReadByte();
                            } while ((z & 0x80) == 0);

                            offset = stream.ReadUInt16();
                            if (match == 0)
                                stream.BaseStream.Seek(offset, SeekOrigin.Begin);
                        }
                        throw new Exception("Unknown param awk call");
                        break;

                    case 0x33:
                        // Copy string into arg1
                        {
                            ushort offset;
                            if (firstBit)
                                offset = stream.ReadByte();
                            else
                                offset = stream.ReadUInt16();
                            offset = vars[offset];
                            offset -= 0x31;

                            // loc_403678
                            byte b;
                            do
                            {
                                b = awkward1();
                                vars[offset] = b;//???
                                offset++;

                                vars[vars[offset++]] = (byte)(b & 0x7f);
                                stream.BaseStream.Seek(-1, SeekOrigin.Current);
                                b = stream.ReadByte();
                            } while ((b & 0x80) == 0);
                        }
                        break;

                    case 0x3A:
                        // "Print" string?
                        {
                            char c;
                            Console.Write("Print string: ");
                            do
                            {
                                c = (char)stream.ReadByte();
                                Console.Write(c);
                            } while ((c & 0x80) == 0);
                            Console.WriteLine("");
                        }
                        break;

                    case 0x36:
                    case 0x34:
                        // Check stream of characters
                        {
                            ushort offset;
                            if (firstBit)
                                offset = stream.ReadByte();
                            else
                                offset = stream.ReadUInt16();

                            // Check stream to see if equal var[offset]
                            byte b;
                            byte match = 0;
                            if (op == 0x34)
                            {
                                do
                                {
                                    b = awkward1();
                                    if (vars[offset] > b)
                                        match = 1;

                                    //offset++;

                                    stream.BaseStream.Seek(-1, SeekOrigin.Current);
                                    b = stream.ReadByte();
                                    // Keep looping till end of string
                                } while ((b & 0x80) == 0);
                            }
                            else
                            {
                                do
                                {
                                    b = awkward1();
                                    if (vars[offset] < (b & 0x7F))
                                        match = 1;
                                    offset++;

                                    stream.BaseStream.Seek(-1, SeekOrigin.Current);
                                    b = stream.ReadByte();
                                    // Keep looping till end of string
                                } while ((b & 0x80) == 0);
                            }

                            offset = stream.ReadUInt16();
                            if (match == 1)
                            {
                                stream.BaseStream.Seek(offset, SeekOrigin.Begin);
                            }
                        }
                        break;

                    case 0x24:
                        // MOV
                        {
                            ushort offset;
                            if (firstBit)
                                offset = stream.ReadByte();
                            else
                                offset = stream.ReadUInt16();

                            vars[offset] = vars[stream.ReadUInt16()];
                        }
                        break;

                    case 0x25:
                        // ADD
                        {
                            ushort offset;
                            if (firstBit)
                                offset = stream.ReadByte();
                            else
                                offset = stream.ReadUInt16();

                            vars[offset] += vars[stream.ReadUInt16()];
                        }
                        break;

                    case 0x29:
                        // Stop MIDI
                        if (music == null)
                            music = new GrvMusic(game);

                        music.stop();
                        break;

                    case 0x2A:
                        // Close things
                        // Close current GJD
                        // Stop midi
                        // Clear hotspots

                        Console.WriteLine("Clean up things here");
                        doThread = false;
                        game.Exit();
                        break;

                    case 0x2E:
                        {
                            ushort offset;
                            if (firstBit)
                                offset = stream.ReadByte();
                            else
                                offset = stream.ReadUInt16();

                            BinaryReader save = new BinaryReader(new FileStream(path + "t7g4win.00" + ((vars[offset]).ToString()), FileMode.Open, FileAccess.Read));
                            vars = save.ReadBytes(vars.Length);
                            save.Close();
                            //throw new Exception("Load game from slot" + vars[offset]);
                            Console.WriteLine("Loading game from slot " + (vars[offset].ToString()) + "?");
                        }
                        break;

                    case 0x2F:
                        {
                            ushort offset;
                            if (firstBit)
                                offset = stream.ReadByte();
                            else
                                offset = stream.ReadUInt16();

                            BinaryWriter save = new BinaryWriter(new FileStream(path + "t7g4win.00" + (vars[offset].ToString()), FileMode.Create, FileAccess.Write));
                            save.Write(vars, 0, vars.Length);
                            save.Close();
                            Console.WriteLine("Save game to slot" + (vars[offset].ToString()) + "?");
                            //throw new Exception("Save game to slot" + vars[offset]);
                        }
                        break;

                    case 0x31:
                        // Set MIDI volume.
                        {
                            ushort arg1 = stream.ReadUInt16(), arg2 = stream.ReadUInt16();
                            if (music == null)
                                music = new GrvMusic(game);

                            if (arg1 == 0)
                            {
                                music.stop();
                            }
                            else
                            {
                                music.setVolume(arg1, arg2);
                            }

                        }
                        break;

                    case 0x32:
                        // JNE
                        {
                            ushort offset;
                            if (firstBit)
                                offset = stream.ReadByte();
                            else
                                offset = stream.ReadUInt16();

                            offset = vars[offset];
                            offset -= 0x31;
                            offset = vars[offset];

                            if (offset != vars[stream.ReadUInt16()])
                            {
                                offset = stream.ReadUInt16();
                                stream.BaseStream.Seek(offset, SeekOrigin.Begin);
                            }
                            else
                                stream.ReadUInt16();
                        }
                        break;

                    case 0x38:
                        // Reload stored stack pointer
                        Console.WriteLine("Reload stored stack pointer - TODO");
                        break;

                    case 0x39:
                        // "Obscure" swap
                        {
                            // .text:0040389B
                            ushort arg1, arg2, arg3;

                            arg1 = stream.ReadByte();
                            arg1 &= 0x7F;

                            if (arg1 == 0x23)
                            {
                                arg1 = stream.ReadByte();
                                arg1 &= 0x7F;
                                arg1 = vars[arg1 - 0x61];
                                arg1 *= 10;
                            }
                            else
                            {
                                arg1 -= 0x30;
                                arg1 *= 10;
                            }

                            arg2 = stream.ReadByte();
                            arg2 &= 0x7F;

                            if (arg2 == 0x23)
                            {
                                arg2 = stream.ReadByte();
                                arg2 &= 0x7F;
                                arg1 += vars[arg2 - 0x61];
                             }
                            else
                            {
                                arg1 = vars[arg1 + arg2];
                            }

                            arg1 += 0x19;

                            // .text:0040394D (loc_40393E + 4lines)

                            arg3 = stream.ReadByte();
                            arg3 &= 0x7F;

                            if (arg3 == 0x23)
                            {
                                arg3 = stream.ReadByte();
                                arg3 &= 0x7F;
                                arg3 = vars[arg3 - 0x61];
                                arg3 *= 10;
                            }
                            else
                            {
                                arg3 -= 0x30;
                                arg3 *= 10;
                            }

                            arg2 = stream.ReadByte();
                            arg2 &= 0x7F;

                            if (arg2 == 0x23)
                            {
                                arg2 = stream.ReadByte();
                                arg2 &= 0x7F;
                                arg3 += vars[arg2 - 0x61];
                            }
                            else
                            {
                                arg3 = vars[arg3 + arg2];
                            }

                            arg3 += 0x19;

                            // now got an arg1 (varOffset2) and arg3 (var13C)

                            byte tmp;
                            tmp = vars[arg1];
                            vars[arg1] = vars[arg2];
                            vars[arg2] = tmp;
                        }
                        break;

                    case 0x3B:
                        // Savegame stuff, inc rendering sphinx.fnt
                        {
                            byte slot = stream.ReadByte();
                            ushort left = stream.ReadUInt16(), top = stream.ReadUInt16(), right = stream.ReadUInt16(), bottom = stream.ReadUInt16();
                            ushort jumpAddress = stream.ReadUInt16();
                            byte cursor = stream.ReadByte();
                            game.addHotspot(new System.Drawing.Rectangle(left, top, right - left, bottom - top), cursor, jumpAddress);
                            //mouseHotspot(left, right, top, bottom, jumpAddress, cursor);
                            Console.WriteLine("Savegame related - slot {0}", slot);
                        }
                        break;

                    case 0x3C:
                        // Check valid savegames
                        {
                            byte count = 0;
                            for (ushort i = 0; i < 0x0A; i++)
                            {
                                if (System.IO.File.Exists(path + "t7g4win.00" + (char)('0' + i)))
                                {
                                    count++;
                                    vars[i] = 1;
                                }
                                else
                                    vars[i] = 0;

                            }
                            vars[0x104] = count;
                        }
                        break;

                    case 0x3D:
                        // Reset 0x00 to 0xFF to zero
                        for (ushort i = 0; i < 0x100; i++)
                            vars[i] = 0;
                        break;

                    case 0x3E:
                        // MOD
                        {
                            Console.WriteLine("Needs work? to trace?");
                            ushort offset;
                            if (firstBit)
                                offset = stream.ReadByte();
                            else
                                offset = stream.ReadUInt16();

                            vars[offset] %= stream.ReadByte();
                        }
                        break;

                    case 0x3F:
                        // Load subscript
                        {
                            Console.WriteLine("Run a subscript");
                            char c;
                            string f = "";
                            do
                            {
                                //t = stream.ReadByte();
                                c = (char)stream.ReadByte();
                                if (c != 0)
                                    f += c;// t.ToString();
                            } while (c != 0);

                            sub = new Groovie(f, game);
                            System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(sub.execute));
                            thread.IsBackground = true;
                            thread.Name = "T7GXNA-SubGroovie";
                            thread.Priority = System.Threading.ThreadPriority.Normal;
                            sub.passParams(ref vars, music, ref flags, vdx);
                            thread.Start();

                            while (thread.IsAlive)
                            {
                                System.Threading.Thread.Sleep(100);
                                //sub.setClick(mouseEvent);
                                if (newLoc >= 0)
                                {
                                    sub.mouseJump(newLoc);
                                    newLoc = -1;
                                }
                            }
                            sub.stop();
                            sub = null;
                        }
                        break;

                    case 0x40:
                        // Two params for VDX player
                        {

                            flags |= bitFlags.Bit7;
                            ushort arg1 = stream.ReadUInt16();
                            if (arg1 > 0x7FFF)
                            {
                                /*
                                 *  .text:00403E62 154         mov     ecx, arg1
                                    .text:00403E68 154         sub     ecx, 10000h     ; Integer Subtraction
                                    .text:00403E6E 154         mov     arg1, ecx

                                 */
                            }
                            stream.ReadUInt16();
                            // Same for arg2
                            throw new Exception("Two VDX player params");
                        }
                        break;

                    case 0x41:
                        // SUB
                        {
                            ushort offset;
                            if (firstBit)
                                offset = stream.ReadByte();
                            else
                                offset = stream.ReadUInt16();

                            vars[offset] -= vars[stream.ReadUInt16()];
                        }
                        break;

                    case 0x42:
                        throw new Exception("Complicated");
                        stream.ReadByte();
                        break;

                    case 0x43:
                        // Return from subscript
                        vars[0x102] = stream.ReadByte();
                        doThread = false;
                        break;

                    case 0x4C:
                        // Save current CD number.  1, 2, -1.
                        {
                            // No drives so no CD
                            if (game.drives.Length == 0)
                            {
                                vars[0x106] = 0xFF;
                                game.chosenDrive = -1;
                                break;
                            }

                            System.IO.DriveInfo[] drives = new DriveInfo[game.drives.Length];
                            for (byte i = 0; i < game.drives.Length; i++)
                            {
                                drives[i] = new DriveInfo(game.drives[i].Name[0].ToString());
                            }
                            game.drives = drives;

                            // Loop through each drive
                            for (byte i = 0; i < drives.Length; i++)
                            {
                                if (!drives[i].IsReady)
                                {
                                    game.chosenDrive = -1;
                                    vars[0x106] = 0xFF;
                                }
                                else if (drives[i].RootDirectory.GetFiles("*.com").Length > 0)
                                {
                                    // CD 2
                                    vars[0x106] = 0x02;
                                    game.chosenDrive = i;
                                    break;
                                }
                                else
                                {
                                    // CD 1
                                    vars[0x106] = 0x01;
                                    game.chosenDrive = i;
                                    break;
                                }
                            }
                            Console.WriteLine("Detect CD " + vars[0x106] + " in drive " + game.chosenDrive);

                        }
                        break;

                    case 0x4D:
                        // If arg1 == 2, play VIE logo, stop midi, play CD
                        if (stream.ReadByte() == 2)
                        {
                            // do some other checks on 0x09 flags?

                            //VDX vdx = new VDX("vielogo", flags, game.GraphicsDevice);
                            //game.addVDX(vdx);
                            //vdx.play();
                            //while (vdx.playing)
                            //    System.Threading.Thread.Sleep(50);
                        }
                        if (music == null)
                            music = new GrvMusic(game);

                        music.CD();
                        
                        break;

                    case 0x52:
                        // Do heap to game area.  Arg discarded?
                        // .text:00403B22
                        stream.ReadByte();
                        throw new Exception("Massively change game area");
                        break;

                    default:
                        throw new Exception("Unknown opcode " + op);
                        break;

                }
            }
        }

        public void passParams(ref byte[] vars, GrvMusic music, ref bitFlags flags, VDX vdx)
        {
            this.vars = vars;
            this.music = music;
            this.flags = flags;
            this.vdx = vdx;
        }

        public void stop()
        {
            vdx.stop();
            music.stop();
            doThread = false;
        }

        private void loadGJD()
        {

        }
        
        /// <summary>
        /// Jump to new groovie script location
        /// </summary>
        /// <param name="location"></param>
        public void mouseJump(long location)
        {
            // Ignore if a previous click is in action
            if (newLoc == -1)
            {
                newLoc = location;
                //Console.WriteLine("Set new groovie location to 0x{0:X}", location);
            }
        }
        
        /// <summary>
        /// Perfrom "awkward" function number 1.  Reads in a variable number of chars depending on vars.
        /// </summary>
        /// <returns>A character</returns>
        private byte awkward1()
        {
            byte t;

            t = stream.ReadByte();
            t &= 0x7F;

            if (t == 0x23)
            {
                t = stream.ReadByte();
                t &= 0x7F;
                t -= 0x61;
                t = vars[t];
            }
            else if (t == 0x7C)
            {
                byte x, y;
                t = stream.ReadByte();

                if (t == 0x23)
                {
                    t = stream.ReadByte();
                    t -= 0x61;
                    x = vars[t];
                }
                else
                {
                    t -= 0x30;
                    x = t;
                }

                t = stream.ReadByte();
                if (t == 0x23)
                {
                    t = stream.ReadByte();
                    t -= 0x61;
                    y = vars[t];
                }
                else
                {
                    t -= 30;
                    y = t;
                }

                x *= 10;
                t = vars[x + y + 0x19];
            }
            else
            {
                t -= 0x30;
            }

            return t;
        }

    }
}
